---
title: Subscriptions in Apollo Kotlin
---

Subscriptions are long-lived GraphQL read operations that can update their response over time, enabling clients to receive new data as it becomes available.

The GraphQL spec does not specify a particular protocol to use for subscription operations. Apollo Kotlin supports the following protocols:

- **WebSocket,** using one of the following subprotocols:
  - [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md) (⚠️ not actively maintained!)
  - [graphql-ws](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md)
  - [appsync](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html) (also uses `graphql-ws` as [Sec-WebSocket-Protocol](https://datatracker.ietf.org/doc/html/rfc6455#section-1.2))
* **HTTP,** using [chunked multipart responses](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md)

**You must use whichever transport is supported by your GraphQL endpoint.**

You define a subscription in your app just like you define a query, except you use the `subscription` keyword. Here's an example subscription for getting the latest value of a number whenever that number is incremented:

```graphql
subscription NumberIncremented {
  numberIncremented
}
```

Unlike with queries and mutations, a subscription operation can include only _one_ field of the `Subscription` type. To subscribe to multiple fields, you create multiple subscription operations.

<Note>

Starting with 4.0.0, Apollo Kotlin provides a new experimental WebSockets implementation that provides better defaults, better retry and error handling APIs. It aims to replace the current implementation in the future.

If you are interested in trying it out, please consult [the Experimental WebSockets page](../advanced/experimental-websockets).

</Note>

## Configuring WebSocket subscriptions

> By default, Apollo Kotlin uses the [`subscriptions-transport-ws`](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md) protocol for subscriptions via the `SubscriptionWsProtocol` class. **This protocol is no longer actively maintained.** It remains the default for backward compatibility purposes.
>
> A future version of Apollo Kotlin will change the default to the newer [`graphql-ws`](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) protocol and `GraphQLWsProtocol` class. If your server already uses `graphql-ws`, make sure to [set your `WsProtocol` to `GraphQLWsProtocol`](#customizing-your-websocket-protocol).

To use subscriptions over WebSocket, use `WebSocketNetworkTransport`:

```kotlin
val apolloClient = ApolloClient.Builder()
    .subscriptionNetworkTransport(
        WebSocketNetworkTransport.Builder()
            .serverUrl("https://example.com/graphql")
            .build()
    )
    .build()
```
<Note>

Apollo Kotlin supports both `https://` (or `http://`) and `wss://` (or `ws://`) protocols. Internally, `wss://` is renamed to `https://` and which one you use does not matter.

</Note>


### Customizing your WebSocket protocol

By default, Apollo Kotlin uses [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md) for backward compatibility purposes, but it supports all the following WebSocket subprotocols:

- [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md) (⚠️ not actively maintained!)
- [graphql-ws](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md)
- [appsync](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html) (also uses `graphql-ws` as [Sec-WebSocket-Protocol](https://datatracker.ietf.org/doc/html/rfc6455#section-1.2))

To customize your protocol, use the `WsProtocol` interface. Apollo Kotlin comes with built-in support for the subprotocols above:

| Subprotocol | Class |
|-------------|-------|
| subscriptions-transport-ws | `SubscriptionWsProtocol` (default) |
| graphql-ws | `GraphQLWsProtocol` |
| appsync | `AppSyncWsProtocol` |

For example, you can configure a `graphql-ws` transport like so:

```kotlin
val apolloClient = ApolloClient.Builder()
    .subscriptionNetworkTransport(
        WebSocketNetworkTransport.Builder()
            .protocol(GraphQLWsProtocol.Factory()) // highlight-line
            .serverUrl("https://example.com/graphql")
            .build()
    )
    .build()
```

### AWS (Amplitude) AppSync

Configuring AppSync is easy but has some subtle nuances due to AppSync's [authorization](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#header-parameter-format-based-on-appsync-api-authorization-mode) and [domain](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#handshake-details-to-establish-the-websocket-connection) requirements.
In particular, AppSync subscriptions on custom domains must append "/realtime" to their endpoint. By contrast non-custom domains do not append "realtime" to the path and instead incorporate it into the domain.

```kotlin
private const val REALTIME = "realtime"

private fun wsUrl(url: String, auth: Map<String, String>): String {
    val realtimeUrl = when {
        url.contains(REALTIME) -> url
        url.contains("amazonaws.com") -> {
            val awsUrlParts = url.split("appsync-api")
            require(awsUrlParts.size == 2) { "Invalid AWS url: $url" }
            "${awsUrlParts[0]}appsync-realtime-api${awsUrlParts[1]}"
        }
        url.endsWith("/") -> "$url$REALTIME"
        else -> "$url/$REALTIME"
    }
    return AppSyncWsProtocol.buildUrl(
        baseUrl = realtimeUrl,
        authorization = auth,
    )
}

// assumes host and key variables have already been set
private fun wsAuth(): Map<String, String> = mapOf("host" to host, "x-api-key" to key)

val apolloClient = ApolloClient.Builder()
    .subscriptionNetworkTransport(
        WebSocketNetworkTransport.Builder()
            // if you know that your URL is correct for realtime then below could be
            // .serverUrl(AppSyncWsProtocol.buildUrl(url, wsAuth())
            .serverUrl(wsUrl(url, wsAuth()))
            .protocol(AppSyncWsProtocol.Factory(connectionPayload = { wsAuth() }))
            .build(),
    )
    .build()
```

### Authentication

Please refer to [this section](../advanced/authentication#authenticating-your-websockets) about authentication with WebSocket.


## Configuring HTTP subscriptions

To use HTTP for subscriptions, use `HttpNetworkTransport` like so:

```kotlin
val apolloClient = ApolloClient.Builder()
    .subscriptionNetworkTransport(
        HttpNetworkTransport.Builder()
            .serverUrl("https://example.com/graphql")
            .build()
    )
    .build()
```

This is the only configuration required. `HttpNetworkTransport` will use [chunked multipart responses](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md) for subscription operations and standard POST or GET requests for queries and mutations.

## Listening to a subscription

After you configure the `NetworkTransport`, use `ApolloClient.subscription` to open the connection and listen for changes:

```kotlin
apolloClient.subscription(TripsBookedSubscription())
    .toFlow()
    .collect {
      println("trips booked: ${it.data?.tripsBooked}")
    }
```

Because subscriptions are long-lasting operations, you should call `toFlow()` to get a `Flow<Response>` instead of a single `Response`.

## Terminating a subscription

Termination is handled through the coroutine scope. Cancel the coroutine to terminate the subscription.

By default, a single WebSocket is shared between all active subscriptions. When no subscription is active, the WebSocket is closed after a configurable timeout.

## Error handling

Like queries, subscriptions support partial responses with GraphQL errors, which are emitted in the `Flow`.

Network errors terminate the `Flow`, and you need to retry to get new updates. Depending on the situation, retrying might open a new WebSocket or restart the subscription.

See also [this section](../advanced/websocket-errors) about WebSocket errors handling.


